/*
 * Douglas Hiura Longo, 06 de Outubro de 2010.
 * 
 * twitter.com/douglashiura
 * java.inf.ufsc.br/dh
 * douglashiura.blogspot.com
 * douglashiura.parprimo.com
 * douglashiura@gmail.com
 * */

/*
 * Está classe não é uma função, tornar ela comparavel atributo esquerda para direita 
 * e direita para esquerda, além de comparar modos nas listas, quando os atributos estão em outras ordens
 * */
package net.douglashiura.picon.comum;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Comparador {
	private boolean igual;
	private Field[] fields;

	public Comparador(Class<?> klass, Object object, Object object2) {
		fields = getFildsDadosAcessavel(klass);
		compare(object, object2);
	}

	private void compare(Object o1, Object o2) {
		igual = allNulls(o1, o2)
				|| (notNulls(o1, o2) && (isEqualInstance(o1, o2) || ehMesmposDados(
						o1, o2)));
	}

	private boolean compareCompost(Object o, Object o2, Field field) {
		return isList(field) ? compareSafeNull((List<?>) o, (List<?>) o2,
				field.getDeclaringClass()) : new Comparador(field.getType(), o,
				o2).igual();
	}

	public boolean igual() {
		return igual;
	}

	private boolean ehMesmposDados(Object o1, Object o2) {
		for (Field field : fields) {
			try {
				if (!compareSafeNull(field.get(o1), field.get(o2), field)) {
					return false;
				}
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
		}
		return true;
	}

	private boolean compareSafeNull(Object o, Object o2, Field field) {
		return allNulls(o, o2) || (notNulls(o, o2) && isEquals(o, o2, field));
	}

	private boolean isEquals(Object o, Object o2, Field field) {
		return isPrimitive(o) ? o.equals(o2) : compareCompost(o, o2, field);
	}

	private boolean isList(Field field) {
		return field.getType().equals(List.class)
				|| field.getType().equals(ArrayList.class);
	}

	private boolean isPrimitive(Object o) {
		return o instanceof String || o instanceof Integer
				|| o instanceof Float || o instanceof Double
				|| o instanceof Date || o instanceof Long
				|| o instanceof Number;
	}

	private boolean isEqualInstance(Object o1, Object o2) {
		return o1 == o2;
	}

	public boolean compareSafeNull(List<?> objects, List<?> objects2,
			Class<?> klass) {
		return allNulls(objects, objects2)
				|| (notNulls(objects, objects2) && (objects.isEmpty() && objects2
						.isEmpty()))
				|| (notNulls(objects, objects2) && ehIgual(objects, objects2,
						klass));

	}

	private boolean ehIgual(List<?> objects, List<?> objects2, Class<?> klass) {
		boolean igual = false;
		for (Object object : objects) {
			for (Object object2 : objects2)
				if (igual = new Comparador(klass, object, object2).igual())
					break;
			if (!igual)
				break;
		}
		return igual;
	}

	private boolean allNulls(Object o1, Object o2) {
		return o1 == null && o2 == null;
	}

	private boolean notNulls(Object o, Object o22) {
		return o != null && o22 != null;
	}

	private Field[] getFildsDadosAcessavel(Class<?> klass) {
		ArrayList<Field> filds = new ArrayList<Field>();
		for (Field field : klass.getDeclaredFields()) {
			field.setAccessible(true);
			try {
				filds.add(field);
			} catch (Exception e) {
			}
		}
		return filds.toArray(new Field[filds.size()]);
	}

}